﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Core.Utility.Helper
{
    /// <summary>
    /// 类型助手类，用于帮助判断object的实际类型
    /// </summary>
    public static class TypeHelper
    {
        /// <summary>
        /// 判断是否是元组类型
        /// </summary>
        /// <param name="type">类型</param>
        /// <returns></returns>
        public static bool IsValueTuple(this Type type)
        {
            return type.Namespace == "System" && type.Name.Contains("ValueTuple`");
        }

        /// <summary>
        /// 判断是否是富基元类型
        /// </summary>
        /// <param name="type">类型</param>
        /// <returns></returns>
        public static bool IsRichPrimitive(this Type type)
        {
            // 处理元组类型
            if (type.IsValueTuple()) return false;

            // 处理数组类型，基元数组类型也可以是基元类型
            if (type.IsArray)
            {
                return type?.GetElementType()?.IsRichPrimitive() ?? false;
            }

            // 基元类型或值类型或字符串类型
            if (type.IsPrimitive || type.IsValueType || type == typeof(string)) return true;

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GenericTypeArguments[0].IsRichPrimitive();

            return false;
        }

        /// <summary>
        /// 判断类型是否实现某个泛型
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="generic">泛型类型</param>
        /// <returns>bool</returns>
        public static bool HasImplementedRawGeneric(this Type? type, Type generic)
        {
            // 检查接口类型
            var isTheRawGenericType = type.GetInterfaces().Any(IsTheRawGenericType);
            if (isTheRawGenericType) return true;

            // 检查类型
            while (type != null && type != typeof(object))
            {
                isTheRawGenericType = IsTheRawGenericType(type);
                if (isTheRawGenericType) return true;
                type = type.BaseType;
            }

            return false;

            // 判断逻辑
            bool IsTheRawGenericType(Type type) => generic == (type.IsGenericType ? type.GetGenericTypeDefinition() : type);
        }


        /// <summary>
        /// 获取所有祖先类型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<Type?> GetAncestorTypes(this Type? type)
        {
            var ancestorTypes = new List<Type?>();
            while (type != null && type != typeof(object))
            {
                if (IsNoObjectBaseType(type))
                {
                    var baseType = type.BaseType;
                    ancestorTypes.Add(baseType);
                    type = baseType;
                }
                else break;
            }

            return ancestorTypes;

            static bool IsNoObjectBaseType(Type type) => type.BaseType != typeof(object);
        }

        /// <summary>
        /// 将一个对象转换为指定类型
        /// </summary>
        /// <param name="obj">待转换的对象</param>
        /// <param name="type">目标类型</param>
        /// <returns>转换后的对象</returns>
        internal static object? ChangeType(this object? obj, Type type)
        {
            if (type == null) return obj;
            if (type == typeof(string)) return obj?.ToString();
            if (type == typeof(Guid) && obj != null) return Guid.Parse(obj?.ToString() ?? "");
            if (type == typeof(bool) && obj != null && obj is not bool)
            {
                var objStr = obj?.ToString()?.ToLower();
                if (objStr == "1" || objStr == "true" || objStr == "yes" || objStr == "on") return true;
                return false;
            }
            if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;

            var underlyingType = Nullable.GetUnderlyingType(type);
            if (type.IsAssignableFrom(obj.GetType())) return obj;
            else if ((underlyingType ?? type).IsEnum)
            {
                if (underlyingType != null && string.IsNullOrWhiteSpace(obj.ToString())) return null;
                else return Enum.Parse(underlyingType ?? type, obj.ToString());
            }
            // 处理DateTime -> DateTimeOffset 类型
            else if (obj.GetType().Equals(typeof(DateTime)) && (underlyingType ?? type).Equals(typeof(DateTimeOffset)))
            {
                return ((DateTime)obj).ConvertToDateTimeOffset();
            }
            // 处理 DateTimeOffset -> DateTime 类型
            else if (obj.GetType().Equals(typeof(DateTimeOffset)) && (underlyingType ?? type).Equals(typeof(DateTime)))
            {
                return ((DateTimeOffset)obj).ConvertToDateTime();
            }
            else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type))
            {
                try
                {
                    return Convert.ChangeType(obj, underlyingType ?? type, null);
                }
                catch
                {
                    return underlyingType == null ? Activator.CreateInstance(type) : null;
                }
            }
            else
            {
                var converter = TypeDescriptor.GetConverter(type);
                if (converter.CanConvertFrom(obj.GetType())) return converter.ConvertFrom(obj);

                var constructor = type.GetConstructor(Type.EmptyTypes);
                if (constructor != null)
                {
                    var o = constructor.Invoke(null);
                    var propertys = type.GetProperties();
                    var oldType = obj.GetType();

                    foreach (var property in propertys)
                    {
                        var p = oldType.GetProperty(property.Name);
                        if (property.CanWrite && p != null && p.CanRead)
                        {
                            property.SetValue(o, ChangeType(p.GetValue(obj, null), property.PropertyType), null);
                        }
                    }
                    return o;
                }
            }
            return obj;
        }

        /// <summary>
        /// 用于判断 <paramref name="obj"/> 是否为指定的 <paramref name="targetType"/>
        /// <para>这通常用于泛型的判断</para>
        /// <code>
        /// //使用示例
        /// obj.IsTargetType(typeof(Dictionary&lt;,&gt;));
        /// </code>
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="targetType">目标类型</param>
        /// <returns></returns>
        public static bool IsTargetType(this object obj,Type targetType)
        {
            if (obj == null)
            {
                return false;
            }
            Type[] typeArgs = obj.GetType().GetGenericArguments();
            if (typeArgs.Length == 0)
            {
                return obj.GetType() == targetType;
            }
            Type genericType = targetType.MakeGenericType(typeArgs);
            return genericType.IsAssignableFrom(obj.GetType());
        }

        /// <summary>
        /// 是否具有某个属性
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static bool HasProperty(this object obj,string propertyName)
        {
            bool res = false;
            Type type = obj.GetType();
            PropertyInfo? propertyInfo = type.GetProperty(propertyName);
            if (propertyInfo != null)
            {
                return true;
            }
            return res;
        }

        /// <summary>
        /// 是否具有某个可读属性
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="bindingAttr">枚举值的按位组合，这些值指定如何进行搜索</param>
        /// <returns></returns>
        public static bool HasReadProperty(this object obj, string propertyName, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic)
        {
            bool res = false;
            Type type = obj.GetType();
            PropertyInfo? propertyInfo = type.GetProperty(propertyName, bindingAttr);
            if (propertyInfo != null)
            {
                if(propertyInfo.CanRead)
                {
                    return true;
                }
            }
            return res;
        }

        /// <summary>
        /// 是否具有某个可写属性
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="bindingAttr">枚举值的按位组合，这些值指定如何进行搜索</param>
        /// <returns></returns>
        public static bool HasWriteProperty(this object obj, string propertyName, BindingFlags bindingAttr  = BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic)
        {
            bool res = false;
            Type type = obj.GetType();
            PropertyInfo? propertyInfo = type.GetProperty(propertyName, bindingAttr);
            if (propertyInfo != null)
            {
                if (propertyInfo.CanWrite)
                {
                    return true;
                }
            }
            return res;
        }

        /// <summary>
        /// 是否具有某个字段
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="fieldName"></param>
        /// <param name="bindingAttr">枚举值的按位组合，这些值指定如何进行搜索</param>
        /// <returns></returns>
        public static bool HasField(this object obj, string fieldName, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic)
        {
            bool res = false;
            Type type = obj.GetType();
            FieldInfo? fieldInfo = type.GetField(fieldName, bindingAttr);
            if (fieldInfo != null)
            {
                return true;
            }
            return res;
        }

        /// <summary>
        /// 是否具有某个方法
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="methodName"></param>
        /// <param name="bindingAttr">枚举值的按位组合，这些值指定如何进行搜索</param>
        /// <returns></returns>
        public static bool HasMethod(this object obj, string methodName, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic)
        {
            Type type = obj.GetType();
            MethodInfo? methodInfo = type.GetMethod(methodName,bindingAttr);
            return methodInfo != null;
        }
    }
}
